package com.logic.modular.gameCore.match.battle.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.comment.pojo.domain.logic.core.CoreCombatFormula;
import com.logic.modular.gameCore.match.battle.mapper.CoreCombatFormulaMapper;
import com.logic.modular.gameCore.match.battle.service.CoreCombatFormulaService;
import lombok.extern.slf4j.Slf4j;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;


/**
 * 战斗公式(CoreCombatFormula)表服务实现类
 *
 * @author cxy
 * @since 2024-08-06 20:28:00
 */
@Slf4j
@Service
@SuppressWarnings("NonAsciiCharacters")
public class CoreCombatFormulaServiceImpl extends ServiceImpl<CoreCombatFormulaMapper, CoreCombatFormula> implements CoreCombatFormulaService {
    Cache<String, Map<Long, CoreCombatFormula>> cache = Caffeine.newBuilder().build();

    @PostConstruct
    private void init() {
        CoreCombatFormula initSelect = CoreCombatFormula.builder().build();
        List<CoreCombatFormula> list = this.list(new LambdaQueryWrapper<>(initSelect));
        log.info("开始缓存战斗公式数据,数据量为:{}", list.size());
        cache.put(
                "coreCombatFormula",
                list.stream().peek(CoreCombatFormula -> CoreCombatFormula.setValues(CoreCombatFormula.valueToValues())).collect(Collectors.toMap(CoreCombatFormula::getId, CoreCombatFormula -> CoreCombatFormula))
        );
        log.info("战斗公式数据缓存完成");
    }

    public List<CoreCombatFormula> listCache() {
        Map<Long, CoreCombatFormula> values = cache.getIfPresent("coreCombatFormula");
        if (Objects.isNull(values)) {
            throw new RuntimeException("战斗公式数据为空");
        }
        return new ArrayList<>(values.values());
    }

    public Map<Long, CoreCombatFormula> mapCache() {
        Map<Long, CoreCombatFormula> values = cache.getIfPresent("coreCombatFormula");
        if (Objects.isNull(values)) {
            throw new RuntimeException("战斗公式数据为空");
        }
        return values;
    }

    public CoreCombatFormula getCache(Long id) {
        Map<Long, CoreCombatFormula> values = cache.getIfPresent("coreCombatFormula");
        if (Objects.isNull(values)) {
            throw new RuntimeException("战斗公式数据为空");
        }
        return values.get(id);
    }


    ///////////////////////////////////////////////////////////////////////////////////////////////////////     物理伤害
    public double getPhysics(
            long 进攻方兵力,
            double 攻击方攻击,
            double 攻击方物理穿透,
            double 攻击方战法伤害率,
            double 攻击方增减伤,
            double 防守方物理防御
    ) {
        double pForces = getPForces(进攻方兵力);
        double pAttack = getPAttack(攻击方攻击, 攻击方战法伤害率, 攻击方增减伤);
        double pMainly = getPMainly(进攻方兵力, 攻击方攻击, 攻击方物理穿透, 攻击方战法伤害率, 攻击方增减伤, 防守方物理防御);
        double value = pForces + pAttack + pMainly;
        // log.info("伤害(物理):{}=兵力伤害({})+攻击伤害({}))+主要伤害({})",value, pForces, pAttack, pMainly);
        return value;
    }

    public double getPForces(Long 进攻方兵力) {
        double[] values = getCache(2L).getValues();

        return values[0] * 进攻方兵力 / (values[1] + 进攻方兵力);
    }

    public double getPAttack(
            double 攻击方攻击,
            double 攻击方战法加成,
            double 攻击方增减伤
    ) {
        String value1 = getCache(3L).getValue();
        String[] split = value1.split("-");
        double start = Double.parseDouble(split[0]);
        double end = Double.parseDouble(split[1]);
        double 攻击伤害随机因子 = RandomUtil.randomDouble(start, end)/100.0;
        double pDamageRate = getPDamageRate(攻击方战法加成);
        double outputCoefficient = getOutputCoefficient(攻击方增减伤);
        long pAttack = (long) (攻击方攻击 * 攻击伤害随机因子 * pDamageRate * outputCoefficient);
        // log.info("物理攻击伤害({}) = 攻击方攻击({}) * 攻击伤害随机因子({}) * 战法指定的伤害率({}) * 输出率(物理增减伤)({})"
        //         ,pAttack, 攻击方攻击,攻击伤害随机因子, pDamageRate, outputCoefficient
        // );
        return pAttack;
    }

    public double getPMainly(
            long 进攻方兵力,
            double 攻击方攻击,
            double 攻击方物理穿透,
            double 攻击方战法加成,
            double 攻击方增减伤,
            double 防守方物理防御
    ) {
        double[] values = getCache(4L).getValues();
        double pDamageRate = getPDamageRate(攻击方战法加成);
        double padCoefficient = getPADCoefficient(攻击方攻击, 防守方物理防御, 攻击方物理穿透);
        double outputCoefficient = getOutputCoefficient(攻击方增减伤);
        double pMainly = values[0] * 进攻方兵力 / (values[1] + 进攻方兵力) * pDamageRate * padCoefficient * outputCoefficient;
        // log.info("主要伤害({}) = values1*进攻方兵力({}*{}) / (values2+进攻方兵力({}+{})) * 战法指定伤害率({}) * 攻防差影响({}) * 输出率(物理增减伤部分)({})",
        //         pMainly,  values[0],    进攻方兵力,       values[1],    进攻方兵力,        pDamageRate,     padCoefficient,     outputCoefficient
        // );
        return pMainly;
    }

    public double getPDamageRate(
            double 攻击方战法指定伤害率
    ) {
        double[] values = getCache(5L).getValues();
        double value1 = values[0];
        double pDamageRate = 攻击方战法指定伤害率 == 0 ? value1 : value1 * 攻击方战法指定伤害率;
        // log.info("物理攻击伤害率({}) = 攻击方战法指定伤害率({})==0? value1({}) : value1({}) * 攻击方战法指定伤害率{};"
        //         ,pDamageRate, 攻击方战法指定伤害率,value1,value1,攻击方战法指定伤害率
        // );
        return pDamageRate;
    }

    public double getPADCoefficient(
            double 攻击方攻击,
            double 攻击方物理穿透,
            double 防守方物理防御
    ) {
        // 6	攻防效果>=0 ? value1-(value2/(value3+攻防效果)) :value4/(value5-攻防效果))	pADCoefficient	物理:攻防影响	300|50000|25000|10000|10000
        double[] values = getCache(6L).getValues();
        double padDifference = getPADDifference(攻击方攻击, 攻击方物理穿透, 防守方物理防御);
        if (padDifference >= 0) {
            return values[0] - (values[1] / (values[2] + padDifference));
        } else {
            return values[3] / (values[4] - padDifference);
        }
    }

    public double getPADDifference(
            double 攻击方攻击,
            double 攻击方物理穿透,
            double 防守方物理防御
    ) {
        // 7	攻击方攻击-防守方防御*(value1-Σ攻击方物理穿透)	pADDifference	物理:攻防效果	100
        double[] values = getCache(7L).getValues();
        return 攻击方攻击 - 防守方物理防御 * (values[0] - 攻击方物理穿透);
    }


    ///////////////////////////////////////////////////////////////////////////////////////////////////////    策略伤害

    public double getStrategy(
            @NotNull @Min(1L) long 进攻方兵力,
            @NotNull double 攻击方策略,
            @NotNull double 攻击方策略穿透,
            @NotNull double 攻击方战法基础伤害率,
            @NotNull double 攻击方战法伤害率成长,
            @NotNull int 是否受策略影响,
            @NotNull double 攻击方增减伤,
            @NotNull double 防守方策略
    ) {
        // 21	value + sAttack + sMainly	strategy	策略伤害
        return getSForces(进攻方兵力) +
                getSAttack(攻击方策略, 攻击方策略穿透, 攻击方战法基础伤害率, 攻击方战法伤害率成长, 是否受策略影响, 攻击方增减伤, 防守方策略) +
                getSMainly(进攻方兵力, 攻击方策略, 攻击方策略穿透, 攻击方战法基础伤害率, 攻击方战法伤害率成长, 是否受策略影响, 攻击方增减伤, 防守方策略);
    }

    // 22	(value1*兵力)/(value2+兵力)	sForces	策略:兵力伤害	17800|645900
    public double getSForces(Long 进攻方兵力) {
        double[] values = getCache(22L).getValues();
        return values[0] * 进攻方兵力 / (values[1] + 进攻方兵力);
    }


    public double getSAttack(
            double 攻击方策略,
            double 攻击方策略穿透,
            double 攻击方战法基础伤害率,
            double 攻击方战法伤害率成长,
            int 是否受策略影响,
            double 攻击方增减伤,
            double 防守方策略
    ) {
        double[] values = getCache(23L).getValues();
        double sDamageRate = getSDamageRate(攻击方策略, 攻击方战法基础伤害率, 攻击方战法伤害率成长, 是否受策略影响);
        double sAPCoefficient = getSAPCoefficient(防守方策略, 攻击方策略穿透);
        double outputCoefficient = getOutputCoefficient(攻击方增减伤);
        // 23	攻击方策略属性 *  value1 * 策略伤害率 * 策略影响 * 输出率 	sAttack	策略:策略伤害	50
        return (long) (攻击方策略 * values[0] * sDamageRate * sAPCoefficient * outputCoefficient);
    }

    public double getSMainly(
            long 进攻方兵力,
            double 攻击方策略,
            double 攻击方策略穿透,
            double 攻击方战法基础伤害率,
            double 攻击方战法伤害率成长,
            int 是否受策略影响,
            double 攻击方增减伤,
            double 防守方策略
    ) {
        // 24	(value1*兵力)/(value2+兵力) * 策略伤害率 * 策略影响 * 输出率	sMainly	策略:主要伤害	30000|350000
        double[] values = getCache(24L).getValues();
        double forces = values[0] * 进攻方兵力 / (values[1] + 进攻方兵力);

        double sDamageRate = getSDamageRate(攻击方策略, 攻击方战法基础伤害率, 攻击方战法伤害率成长, 是否受策略影响);
        double sAPCoefficient = getSAPCoefficient(防守方策略, 攻击方策略穿透);
        double outputCoefficient = getOutputCoefficient(攻击方增减伤);
        return forces * sDamageRate * sAPCoefficient * outputCoefficient;
    }

    public double getSDamageRate(
            double 攻击方策略,
            double 攻击方战法基础伤害率,
            double 攻击方战法伤害率成长,
            int 是否受策略影响
    ) {
        double[] values = getCache(25L).getValues();
        // 25	"is_magic==1?
        // 战法基础伤害率 + (策略 - value1)*影响系数(magic_influence)/100:
        // 战法基础伤害率				"	sDamageRate	策略:策略伤害率	8000	(is_magic)是否受策略影响,影响系数(magic_influence)
        boolean b = 是否受策略影响 == 1;
        double value1 = 攻击方战法基础伤害率 + (攻击方策略 - values[0]) * 攻击方战法伤害率成长 / 100;
        return b ? value1 : 攻击方战法基础伤害率;
    }

    public double getSAPCoefficient(
            double 攻击方策略穿透,
            double 防守方策略
    ) {
        double[] values = getCache(26L).getValues();
        double sapDifference = getSAPDifference(攻击方策略穿透, 防守方策略);
        // 26	策略效果>= value1 ?
        if (sapDifference >= values[0]) {
            // ( 1- (value2-(value3/(value2+防守方策略)))/100) :
            return (1 - (values[1] - (values[2] / (values[1] + sapDifference))) / 100);
        } else {
            //  1	sAPCoefficient	策略:策略影响	5000|7500|937500
            return 1;
        }
    }

    public double getSAPDifference(
            double 攻击方策略穿透,
            double 防守方策略
    ) {
        double[] values = getCache(27L).getValues();
        // 27	防守方策略 * (value1-Σ攻击方策略穿透)	sAPDifference	策略:策略效果	100
        return 防守方策略 * (values[0] - 攻击方策略穿透);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////     恢复
    public double getRealRecover(
            @NotNull long 攻击方兵力,
            @NotNull double 攻击方策略,
            @NotNull double 增减伤,
            @NotNull double 目标方上回合死亡兵力,
            @NotNull double 目标方当前回合死亡兵力,
            // 战法
            @NotNull double 攻击方基础恢复率,
            @NotNull double 攻击方恢率复增长率,
            @NotNull int 是否受策略影响
    ) {
        // 31	恢复值>最大恢复值?最大恢复值:恢复值		恢复兵力
        double maxRecover = getMaxRecover(目标方上回合死亡兵力, 目标方当前回合死亡兵力);
        double recover = getRecover(攻击方兵力, 增减伤, 攻击方基础恢复率, 攻击方恢率复增长率, 是否受策略影响, 攻击方策略);
        return Math.min(maxRecover, recover);
    }

    public double getRecover(
            long 进攻方兵力,
            double 增减伤,
            double 攻击方基础恢复率,
            double 攻击方恢率复增长率,
            int 是否受策略影响,
            double 攻击方策略
    ) {
        // 32	(vaue1 * 兵力)/ (value2 + 兵力) *  输出率  *  恢复效果		恢复值	30000|350000
        double[] values = getCache(32L).getValues();
        double outputCoefficient = getOutputCoefficient(增减伤);
        double recoverEffect = getRecoverEffect(攻击方基础恢复率, 攻击方恢率复增长率, 是否受策略影响, 攻击方策略);
        return values[0] * 进攻方兵力 / (values[1] + 进攻方兵力) * outputCoefficient * recoverEffect;
    }

    public double getRecoverEffect(
            double 攻击方基础恢复率,
            double 攻击方恢率复增长率,
            int 是否受策略影响,
            double 攻击方策略
    ) {
        double[] values = getCache(33L).getValues();
        // 33	"基础恢复率+恢率复增长率*(策略 - value1)/100		"		恢复:恢复效果	8000
        boolean b = 是否受策略影响 == 1;
        double value1 = 攻击方基础恢复率 + 攻击方恢率复增长率 * ((攻击方策略 - values[0]) / 100);
        return b ? value1 : 攻击方基础恢复率;
    }

    public double getMaxRecover(
            double 攻击方上回合死亡兵力,
            double 攻击方当前回合死亡兵力
    ) {
        double[] values = getCache(34L).getValues();
        // 34	回合n-1伤兵 * value + 回合n 伤兵		最大恢复值	87
        return getWoundedSoldier(攻击方上回合死亡兵力) * values[0] + getWoundedSoldier(攻击方当前回合死亡兵力);
    }

    public double getWoundedSoldier(double 死亡兵力) {
        double[] values = getCache(35L).getValues();
        // 35	死亡兵力*90%		伤兵	90
        return 死亡兵力 * values[0];
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////     dot
    public double physicsDotHurt(
            long 进攻方兵力,
            double 攻击方攻击,
            double 攻击方物理穿透,
            double 攻击方战法加成,
            double 攻击方增减伤,
            double 防守方防御
    ) {
        CoreCombatFormula physics = getCache(42L);
        double[] physicsValues = physics.getValues();
        // 42	物理:兵力伤害 * 1/3   + 攻击属性 * value1 * 物理伤害率 * 输出率 + 物理:主要伤害		dot物理	40
        double pForces = getPForces(进攻方兵力) * 1 / 3.0; // 兵力伤害 * 1/3
        double pAttack = 攻击方攻击 * physicsValues[0] * getPDamageRate(攻击方战法加成) * getOutputCoefficient(攻击方增减伤);// 攻击属性 * value1 * 物理伤害率 * 输出率
        double pMainly = getPMainly(进攻方兵力, 攻击方战法加成, 攻击方攻击, 防守方防御, 攻击方物理穿透, 攻击方增减伤);

        return pForces + pAttack + pMainly;
    }

    public double strategyDotHurt(
            long 进攻方兵力,
            double 攻击方策略,
            double 攻击方策略穿透,
            double 攻击方战法基础伤害率,
            double 攻击方战法伤害率成长,
            int 是否受策略影响,
            double 攻击方增减伤,
            double 防守方策略
    ) {
        CoreCombatFormula strategy = getCache(43L);
        double[] strategyValues = strategy.getValues();

        // 43	策略:兵力伤害 * 1/3   + 策略值 * value1 * 策略伤害率 * 输出率 * 策略影响 + 策略主要伤害		dot策略	25
        double sForces = getSForces(进攻方兵力) * 1 / 3.0;
        double sAttack = 攻击方策略 * strategyValues[0]
                * getSDamageRate(攻击方策略, 攻击方战法基础伤害率, 攻击方战法伤害率成长, 是否受策略影响)
                * getOutputCoefficient(攻击方增减伤)
                * getSAPCoefficient(防守方策略, 攻击方策略穿透);
        double sMainly = getSMainly(进攻方兵力, 攻击方策略, 攻击方策略穿透, 攻击方战法基础伤害率, 攻击方战法伤害率成长, 是否受策略影响, 攻击方增减伤, 防守方策略);

        return sForces + sAttack + sMainly;
    }


    //////////////////////////////////////////////////////////////////////////////////////////////////////   输出率
    public double getOutputCoefficient(
            double 增减伤
    ) {
        CoreCombatFormula coreCombatFormula = getCache(51L);
        double[] values = coreCombatFormula.getValues();
        double max = Math.max(values[0], values[1] + 增减伤);
        // log.info("输出率(增减伤)({})：max(value1(最低增减伤)({}) , value2(基础增减伤){} + Σ增减伤({}))", max,values[0],values[1],增减伤);
        return max;
    }
}
